package com.kexl.activiti.demo.controller;

import cn.hutool.core.codec.Base64;
import com.kexl.activiti.demo.service.ActivitiService;
import com.kexl.activiti.demo.utils.ActivitiUtil;
import com.kexl.activiti.demo.utils.activiti.ActivitiUtils;
import lombok.extern.slf4j.Slf4j;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricActivityInstanceQuery;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.impl.RepositoryServiceImpl;
import org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.activiti.engine.impl.context.Context;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.util.IoUtil;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.runtime.ProcessInstanceQuery;
import org.activiti.engine.task.Task;
import org.activiti.image.ProcessDiagramGenerator;
import org.activiti.image.impl.DefaultProcessDiagramGenerator;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @classname: TestActivitiController
 * @description: 创建一个请假流程实例
 * @author: kexl
 * @create: 2021-03-15 16:21
 * @version: 1.0
 **/
@RestController
@Slf4j
public class TestActivitiController {

    @Autowired
    private ActivitiService activitiService;

    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private TaskService taskService;

    @Autowired
    private HistoryService historyService;

    @Autowired
    ProcessEngineConfiguration processEngineConfiguration;


    /**
     * 1.创建一个请假流程实例
     */
    @GetMapping("createLeaveFlow")
    public void createLeaveFlow() throws IOException {
         //1.举个例子，soup_tang 需要请假 ，我在页面上添加请假申请,任务申请人是soup_tang,
        // 我选择审核人为汤总,请汤总给我审核这个请假流程。
        String checkPeople = "汤总";
        activitiService.createLeaveWorkFlow(checkPeople);
    }
    /**
     * 2.执行完 第一个方法的创建流程以后，就要执行下面这个审核方法
     */
    @GetMapping(value = "assginLeaveFlow")
    public void assginLeaveFlow(String instanceId)  {
        //1.当汤总 登录系统的时候，他需要审核一些 审核人为他的 请假流程,当然了，我们在创建这个请假流程的时候，
        //我这个审核人就是可以动态赋值的
        //2.这个是流程实例id ,每次创建一个工作流，都会生成，在具体的生产环境中我们需要将这个流程实例id保存起来。（2507 可能需要替换喔 切记。）
        String processInstanceId = instanceId;
        //3.这个是审核人是否同意当前这个流程。（0代表同意 1代表不同意）
        int isAcceppt = 0;
        //4.这个是当前审核人
        String checkPeople = "汤总";
        activitiService.assginLeaveWorkFlow(processInstanceId,isAcceppt,checkPeople);
    }
    /**
     * 查询正在运行的实例
     */
    @GetMapping(value = "listLeaveFlow")
    public void listLeaveFlow()  {
        activitiService.queryExcution();
    }

    /**
     * 3.查询正在运行的实例
     */
    @GetMapping(value = "getLeaveFlowByProccessInstanceId")
    public void getLeaveFlowByProccessInstanceId(String instanceId)  {
        String proccessInstanceId =instanceId;
        activitiService.queryProccessInstanceState(proccessInstanceId);
    }

    /**
     * <p>查看当前流程图</p>
     * @param instanceId 流程实例
     * @param response void 响应
     * @author FRH
     * @time 2018年12月10日上午11:14:12
     * @version 1.0
     */
    @RequestMapping(value="showImg")
    public void showImg(String instanceId, HttpServletResponse response) {
        /*
         * 参数校验
         */
        log.info("查看完整流程图！流程实例ID:{}", instanceId);
        if(StringUtils.isBlank(instanceId)) return ;
        /*
         *  获取流程实例
         */
        HistoricProcessInstance processInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(instanceId).singleResult();
        if(processInstance == null) {
            log.error("流程实例ID:{}没查询到流程实例！", instanceId);
            return ;
        }
        // 根据流程对象获取流程对象模型
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());
        /*
         *  查看已执行的节点集合
         *  获取流程历史中已执行节点，并按照节点在流程中执行先后顺序排序
         */
        // 构造历史流程查询
        HistoricActivityInstanceQuery historyInstanceQuery = historyService.createHistoricActivityInstanceQuery().processInstanceId(instanceId);
        // 查询历史节点
        List<HistoricActivityInstance> historicActivityInstanceList = historyInstanceQuery.orderByHistoricActivityInstanceStartTime().asc().list();
        if(historicActivityInstanceList == null || historicActivityInstanceList.size() == 0) {
            log.info("流程实例ID:{}没有历史节点信息！", instanceId);
            outputImg(response, bpmnModel, null, null);
            return ;
        }
        // 已执行的节点ID集合(将historicActivityInstanceList中元素的activityId字段取出封装到executedActivityIdList)
        List<String> executedActivityIdList = historicActivityInstanceList.stream().map(item -> item.getActivityId()).collect(Collectors.toList());
        /*
         *  获取流程走过的线
         */
        // 获取流程定义
        ProcessDefinitionEntity processDefinition = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService).getDeployedProcessDefinition(processInstance.getProcessDefinitionId());
        List<String> flowIds = ActivitiUtils.getHighLightedFlows(bpmnModel, processDefinition, historicActivityInstanceList);
        /*
         * 输出图像，并设置高亮
         */
        outputImg(response, bpmnModel, flowIds, executedActivityIdList);
    }


    /**
     * <p>输出图像</p>
     * @param response 响应实体
     * @param bpmnModel 图像对象
     * @param flowIds 已执行的线集合
     * @param executedActivityIdList void 已执行的节点ID集合
     * @author FRH
     * @time 2018年12月10日上午11:23:01
     * @version 1.0
     */
    private void outputImg(HttpServletResponse response,BpmnModel bpmnModel, List<String> flowIds, List<String> executedActivityIdList) {
        InputStream imageStream = null;
        try {
            ProcessDiagramGenerator processDiagramGenerator = null;
            // 使用默认的程序图片生成器
            processDiagramGenerator = new DefaultProcessDiagramGenerator();

            // 使用默认配置获得流程图表生成器，并生成追踪图片字符流
            imageStream = processDiagramGenerator.generateDiagram(bpmnModel, executedActivityIdList, flowIds, "宋体", "微软雅黑", "黑体",true, "png");

            byte[] b = new byte[1024];
            int len;
            while ((len = imageStream.read(b, 0, 1024)) != -1) {
                response.getOutputStream().write(b, 0, len);
            }
            response.getOutputStream().flush();
        } catch (Exception e) {
            log.error("流程图输出异常！", e);
        } finally { // 流关闭
            IoUtil.closeSilently(imageStream);
        }
    }

    /**
     * 查询历史流程 自定义颜色
     * @param instanceId
     * @param response
     * @throws IOException
     */
    @RequestMapping("img")
    public void image1(String instanceId, HttpServletResponse response) throws IOException {
        ActivitiUtil.getFlowImgByInstanceId(instanceId,response.getOutputStream(),true);
    }

    /**
     * <p>启动请假流程</p>
     * @return String 流程实例ID
     * @author FRH
     * @time 2018年12月10日上午11:03:36
     * @version 1.0
     */
    @RequestMapping(value="start")
    @ResponseBody
    public String start() {
        // xml中定义的ID
        String instanceKey = "leaveProcess";
        log.info("开启请假流程...");

        // 设置流程参数，开启流程
        Map<String,Object> map = new HashMap<String,Object>();
        map.put("jobNumber","A1001");
        map.put("busData","bus data");
        ProcessInstance instance = runtimeService.startProcessInstanceByKey(instanceKey, map);//使用流程定义的key启动流程实例，key对应helloworld.bpmn文件中id的属性值，使用key值启动，默认是按照最新版本的流程定义启动

        log.info("启动流程实例成功:{}", instance);
        log.info("流程实例ID:{}", instance.getId());
        log.info("流程定义ID:{}", instance.getProcessDefinitionId());

        //验证是否启动成功
        //通过查询正在运行的流程实例来判断
        ProcessInstanceQuery processInstanceQuery = runtimeService.createProcessInstanceQuery();
        //根据流程实例ID来查询
        List<ProcessInstance> runningList = processInstanceQuery.processInstanceId(instance.getProcessInstanceId()).list();
        log.info("根据流程ID查询条数:{}", runningList.size());
        // 返回流程ID
        return instance.getId();
    }

    /**
     * <p>员工提交申请</p>
     * @param request 请求
     * @return String 申请受理结果
     * @author FRH
     * @time 2018年12月10日上午11:15:09
     * @version 1.0
     */
    @RequestMapping(value="/employeeApply")
    @ResponseBody
    public String employeeApply(HttpServletRequest request){
        /*
         * 获取请求参数
         */
        String taskId = request.getParameter("taskId"); // 任务ID
        String jobNumber = request.getParameter("jobNumber"); // 工号
        String leaveDays = request.getParameter("leaveDays"); // 请假天数
        String leaveReason = request.getParameter("leaveReason"); // 请假原因

        /*
         *  查询任务
         */
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if(task == null) {
            log.info("任务ID:{}查询到任务为空！", taskId);
            return "fail";
        }
        /*
         * 参数传递并提交申请
         */
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("days", leaveDays);
        map.put("date", new Date());
        map.put("reason", leaveReason);
        map.put("jobNumber", jobNumber);
        taskService.complete(task.getId(), map);
        log.info("执行【员工申请】环节，流程推动到【上级审核】环节");

        /*
         * 返回成功
         */
        return "success";
    }

    /**
     * <p>查看任务</p>
     * @param request 请求
     * @return String  任务展示页面
     * @author FRH
     * @time 2018年12月10日上午11:21:33
     * @version 1.0
     */
    @RequestMapping(value="toShowTask")
    public String toShowTask(HttpServletRequest request) {
        /*
         * 获取请求参数
         */
        List<Task> taskList = taskService.createTaskQuery().list();
        if(taskList == null || taskList.size() == 0) {
            log.info("查询任务列表为空！");
            return "/task";
        }
        /*
         * 查询所有任务，并封装
         */
        List<Map<String, String>> resultList = new ArrayList<Map<String, String>>();
        for(Task task : taskList) {
            Map<String, String> map = new HashMap<String, String>();
            map.put("taskId", task.getId());
            map.put("name", task.getName());
            map.put("createTime", task.getCreateTime().toString());
            map.put("assignee", task.getAssignee());
            map.put("instanceId", task.getProcessInstanceId());
            map.put("executionId", task.getExecutionId());
            map.put("definitionId", task.getProcessDefinitionId());
            resultList.add(map);
        }
        /*
         * 返回结果
         */
        log.info("返回集合:{}", resultList.toString());
        request.setAttribute("resultList", resultList);
        return "/task";
    }

    /**
     * <p>判断流程是否完成</p>
     * @param processInstanceId 流程实例ID
     * @return boolean 已完成-true，未完成-false
     * @author FRH
     * @time 2018年12月10日上午11:23:26
     * @version 1.0
     */
    public boolean isFinished(String processInstanceId) {
        return historyService.createHistoricProcessInstanceQuery().finished().processInstanceId(processInstanceId).count() > 0;
    }
}

